]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
shellDBus: Add a real error reporting system to InstallExtensionRemote
authorJasper St. Pierre <jstpierre@mecheye.net>
Sat, 16 Jun 2012 03:20:36 +0000 (23:20 -0400)
committerJasper St. Pierre <jstpierre@mecheye.net>
Mon, 2 Jul 2012 22:29:56 +0000 (18:29 -0400)
Instead of using the 'extension-state-changed' signal to relay errors,
use DBus's native error mechanism to inform the method caller that the
call has failed. This requires making the method actually asynchronous
so that we don't block the browser, which is stuck waiting for a reply
from the browser plugin. To ensure this, we need to modify the browser
plugin API to ensure its extesion installation method is asynchronous.

Additionally, this lets us remove the awful, broken hacks that we used
when a user clicked the "Cancel" button, replacing it by a DBus return
value.

https://bugzilla.gnome.org/show_bug.cgi?id=679099

browser-plugin/browser-plugin.c
js/ui/extensionDownloader.js
js/ui/shellDBus.js

index 6daea9963d780762416d98e8d1b3af3d12c895f9..b47b41c0e17a4ebb5b0259147bdcfcd8059aee8d 100644 (file)
@@ -41,7 +41,7 @@
       "It can be used only by extensions.gnome.org"
 #define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
 
-#define PLUGIN_API_VERSION 4
+#define PLUGIN_API_VERSION 5
 
 typedef struct {
   GDBusProxy *proxy;
@@ -489,6 +489,12 @@ parse_args (const gchar     *format_str,
 
           *(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg);
           break;
+
+        case 'o':
+          if (!NPVARIANT_IS_OBJECT (arg))
+            goto out;
+
+          *(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg);
         }
     }
 
@@ -580,6 +586,53 @@ plugin_enable_extension (PluginObject    *obj,
   return ret;
 }
 
+typedef struct _AsyncClosure AsyncClosure;
+
+struct _AsyncClosure {
+  PluginObject *obj;
+  NPObject *callback;
+  NPObject *errback;
+};
+
+static void
+install_extension_cb (GObject      *proxy,
+                      GAsyncResult *async_res,
+                      gpointer      user_data)
+{
+  AsyncClosure *async_closure = (AsyncClosure *) user_data;
+  GError *error = NULL;
+  GVariant *res;
+  NPVariant args[1];
+  NPVariant result = { NPVariantType_Void };
+  NPObject *callback;
+
+  res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
+
+  if (res == NULL)
+    {
+      if (g_dbus_error_is_remote_error (error))
+        g_dbus_error_strip_remote_error (error);
+      STRINGZ_TO_NPVARIANT (error->message, args[0]);
+      callback = async_closure->errback;
+    }
+  else
+    {
+      char *string_result;
+      g_variant_get (res, "(&s)", &string_result);
+      STRINGZ_TO_NPVARIANT (string_result, args[0]);
+      callback = async_closure->callback;
+    }
+
+  funcs.invokeDefault (async_closure->obj->instance,
+                       callback, args, 1, &result);
+
+  funcs.releasevariantvalue (&result);
+
+  funcs.releaseobject (async_closure->callback);
+  funcs.releaseobject (async_closure->errback);
+  g_slice_free (AsyncClosure, async_closure);
+}
+
 static gboolean
 plugin_install_extension (PluginObject    *obj,
                           uint32_t         argc,
@@ -587,18 +640,25 @@ plugin_install_extension (PluginObject    *obj,
                           NPVariant       *result)
 {
   gchar *uuid;
+  NPObject *callback, *errback;
+  AsyncClosure *async_closure;
 
-  if (!parse_args ("u", argc, argv, &uuid))
+  if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
     return FALSE;
 
+  async_closure = g_slice_new (AsyncClosure);
+  async_closure->obj = obj;
+  async_closure->callback = funcs.retainobject (callback);
+  async_closure->errback = funcs.retainobject (errback);
+
   g_dbus_proxy_call (obj->proxy,
                      "InstallRemoteExtension",
                      g_variant_new ("(s)", uuid),
                      G_DBUS_CALL_FLAGS_NONE,
                      -1, /* timeout */
                      NULL, /* cancellable */
-                     NULL, /* callback */
-                     NULL /* user_data */);
+                     install_extension_cb,
+                     async_closure);
 
   g_free (uuid);
 
index e9f49524c77c681e05f2a4882cc3272f08472a14..1d4c468470ddcb7f5647ad2595cb8d3422a8204b 100644 (file)
@@ -22,7 +22,7 @@ const REPOSITORY_URL_INFO =     REPOSITORY_URL_BASE + '/extension-info/';
 
 let _httpSession;
 
-function installExtensionFromUUID(uuid) {
+function installExtensionFromUUID(uuid, invocation) {
     let params = { uuid: uuid,
                    shell_version: Config.PACKAGE_VERSION };
 
@@ -31,6 +31,7 @@ function installExtensionFromUUID(uuid) {
     _httpSession.queue_message(message, function(session, message) {
         if (message.status_code != Soup.KnownStatusCode.OK) {
             ExtensionSystem.logExtensionError(uuid, 'downloading info: ' + message.status_code);
+            invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString());
             return;
         }
 
@@ -39,10 +40,11 @@ function installExtensionFromUUID(uuid) {
             info = JSON.parse(message.response_body.data);
         } catch (e) {
             ExtensionSystem.logExtensionError(uuid, 'parsing info: ' + e);
+            invocation.return_dbus_error('org.gnome.Shell.ParseInfoError', e.toString());
             return;
         }
 
-        let dialog = new InstallExtensionDialog(uuid, info);
+        let dialog = new InstallExtensionDialog(uuid, info, invocation);
         dialog.open(global.get_current_time());
     });
 }
@@ -63,9 +65,9 @@ function uninstallExtensionFromUUID(uuid) {
     return true;
 }
 
-function gotExtensionZipFile(session, message, uuid) {
+function gotExtensionZipFile(session, message, uuid, callback, errback) {
     if (message.status_code != Soup.KnownStatusCode.OK) {
-        ExtensionSystem.logExtensionError(uuid, 'downloading extension: ' + message.status_code);
+        errback('DownloadExtensionError', message.status_code);
         return;
     }
 
@@ -74,7 +76,7 @@ function gotExtensionZipFile(session, message, uuid) {
         if (!dir.query_exists(null))
             dir.make_directory_with_parents(null);
     } catch (e) {
-        ExtensionSystem.logExtensionError(uuid, 'Could not create extension directory');
+        errback('CreateExtensionDirectoryError', e);
         return;
     }
 
@@ -89,7 +91,7 @@ function gotExtensionZipFile(session, message, uuid) {
                                           null);
 
     if (!success) {
-        ExtensionSystem.logExtensionError(uuid, 'extract: could not extract');
+        errback('ExtractExtensionError');
         return;
     }
 
@@ -97,8 +99,7 @@ function gotExtensionZipFile(session, message, uuid) {
         GLib.spawn_close_pid(pid);
 
         if (status != 0) {
-            ExtensionSystem.logExtensionError(uuid, 'extract: could not extract');
-            invocation.return_dbus_error('org.gnome.Shell.ExtractExtensionError', '');
+            errback('ExtractExtensionError');
             return;
         }
 
@@ -111,6 +112,7 @@ function gotExtensionZipFile(session, message, uuid) {
 
         let extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
         ExtensionSystem.loadExtension(extension);
+        callback();
     });
 }
 
@@ -118,11 +120,12 @@ const InstallExtensionDialog = new Lang.Class({
     Name: 'InstallExtensionDialog',
     Extends: ModalDialog.ModalDialog,
 
-    _init: function(uuid, info) {
+    _init: function(uuid, info, invocation) {
         this.parent({ styleClass: 'extension-dialog' });
 
         this._uuid = uuid;
         this._info = info;
+        this._invocation = invocation;
 
         this.setButtons([{ label: _("Cancel"),
                            action: Lang.bind(this, this._onCancelButtonPressed),
@@ -147,34 +150,27 @@ const InstallExtensionDialog = new Lang.Class({
 
     _onCancelButtonPressed: function(button, event) {
         this.close(global.get_current_time());
-
-        // Even though the extension is already "uninstalled", send through
-        // a state-changed signal for any users who want to know if the install
-        // went through correctly -- using proper async DBus would block more
-        // traditional clients like the plugin
-        let meta = { uuid: this._uuid,
-                     state: ExtensionSystem.ExtensionState.UNINSTALLED,
-                     error: '' };
-
-        _signals.emit('extension-state-changed', meta);
+        this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
     },
 
     _onInstallButtonPressed: function(button, event) {
-        let state = { uuid: this._uuid,
-                      state: ExtensionSystem.ExtensionState.DOWNLOADING,
-                      error: '' };
-
-        _signals.emit('extension-state-changed', state);
-
         let params = { shell_version: Config.PACKAGE_VERSION };
 
         let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
         let message = Soup.form_request_new_from_hash('GET', url, params);
 
-        _httpSession.queue_message(message,
-                                   Lang.bind(this, function(session, message) {
-                                       gotExtensionZipFile(session, message, this._uuid);
-                                   }));
+        let invocation = this._invocation;
+        function errback(code, message) {
+            invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : '');
+        }
+
+        function callback() {
+            invocation.return_value(GLib.Variant.new('(s)', 'successful'));
+        }
+
+        _httpSession.queue_message(message, Lang.bind(this, function(session, message) {
+            gotExtensionZipFile(session, message, this._uuid, callback, errback);
+        }));
 
         this.close(global.get_current_time());
     }
index 910d5cfc8113a7784d2afbb1c8bcebce41a3b3a4..e402cabbbdf6011b4d0992f7e50b7f720ad8374e 100644 (file)
@@ -202,6 +202,7 @@ const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
 </signal>
 <method name="InstallRemoteExtension">
     <arg type="s" direction="in" name="uuid"/>
+    <arg type="s" direction="out" name="result"/>
 </method>
 <method name="UninstallExtension">
     <arg type="s" direction="in" name="uuid"/>
@@ -285,8 +286,8 @@ const GnomeShellExtensions = new Lang.Class({
         return extension.errors;
     },
 
-    InstallRemoteExtension: function(uuid) {
-        ExtensionDownloader.installExtensionFromUUID(uuid);
+    InstallRemoteExtensionAsync: function([uuid], invocation) {
+        return ExtensionDownloader.installExtensionFromUUID(uuid, invocation);
     },
 
     UninstallExtension: function(uuid) {